﻿@page "/identity/users"
@using ActualLab.Fusion
@using CleanArchitecture.Blazor.Application.Features.Fusion
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.UserActivation
@using CleanArchitecture.Blazor.Infrastructure.Persistence
@using CleanArchitecture.Blazor.Infrastructure.Services.MultiTenant
@using CleanArchitecture.Blazor.Server.UI.Components.UtcToLocalTime
@using Microsoft.AspNetCore.WebUtilities
@using CleanArchitecture.Blazor.Domain.Identity
@using CleanArchitecture.Blazor.Application.Features.Identity.DTOs
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.ResetPassword
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.SendWelcome
@using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity
@using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant
@using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Roles.Components
@using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Users.Components
@using System.Linq.Expressions
@using CleanArchitecture.Blazor.Infrastructure.Constants.Role
@using CleanArchitecture.Blazor.Infrastructure.Constants.ClaimTypes
@using System.Security.Claims
@using System.Globalization
@using ZiggyCreatures.Caching.Fusion

@attribute [Authorize(Policy = Permissions.Users.View)]
@inherits OwningComponentBase<ApplicationDbContext>
@implements IDisposable

@inject IOnlineUserTracker OnlineUserTracker
@inject BlazorDownloadFileService DownloadFileService
@inject IUserService UserService
@inject ITenantService TenantService
@inject IFusionCache FusionCache
@inject IExcelService ExcelService
@inject IMailService MailService
@inject IRoleService RoleService
@inject IStringLocalizer<Users> Localizer
@inject ILogger<Users> Logger

<PageTitle>@_title</PageTitle>

<MudDataGrid T="ApplicationUserDto" @ref="_dataGrid"
             FixedHeader="true"
             FixedFooter="false"
             @bind-RowsPerPage="_defaultPageSize"
             Hover="true"
             SelectOnRowClick="false"
             MultiSelection="true"
             @bind-SelectedItems="_selectedUsers"
             Loading="@_isLoading"
             ServerData="@(LoadServerData)">
    <ToolBarContent>
        <MudStack Row Spacing="0" Class="flex-grow-1" Justify="Justify.SpaceBetween">
            <MudStack Row AlignItems="AlignItems.Start">
                <MudIcon Icon="@Icons.Material.Filled.SwitchAccount" Size="Size.Large" />
                <MudStack Spacing="0">
                    <MudText Typo="Typo.subtitle2" Class="mb-2">@_title</MudText>
                    <MudSelect T="string" Style="min-width:120px" ValueChanged="TenantListViewChanged" Value="@_selectedTenantId" Dense="true" Label="@Localizer["List View"]">
                        <MudSelectItem T="string" Value="@string.Empty">@Localizer["ALL"]</MudSelectItem>
                        @foreach (var tenant in TenantService.DataSource)
                        {
                            <MudSelectItem T="string" Value="@tenant.Id">@tenant.Name</MudSelectItem>
                        }
                    </MudSelect>
                </MudStack>
            </MudStack>
            <MudStack Spacing="0" AlignItems="AlignItems.End">
                <MudToolBar Dense WrapContent="true" Class="py-1 px-0">
                    <MudButton Disabled="@_isLoading"
                               OnClick="RefreshDataGrid"
                               StartIcon="@Icons.Material.Outlined.Refresh">
                        @ConstantString.Refresh
                    </MudButton>
                    @if (_accessRights.Create)
                    {
                        <MudButton StartIcon="@Icons.Material.Outlined.Add"
                                   OnClick="CreateUser">
                            @ConstantString.New
                        </MudButton>
                    }
                    <MudMenu TransformOrigin="Origin.BottomRight" AnchorOrigin="Origin.BottomRight" EndIcon="@Icons.Material.Filled.MoreVert" Label="@ConstantString.More">
                        @if (_accessRights.Delete)
                        {
                            <MudMenuItem Disabled="@(!_selectedUsers.Any())"
                                         OnClick="DeleteCheckedUsersAsync">
                                @ConstantString.Delete
                            </MudMenuItem>
                        }
                        @if (_accessRights.Export)
                        {
                            <MudMenuItem Disabled="@_isExporting"
                                         OnClick="ExportUsersAsync">
                                @ConstantString.Export
                            </MudMenuItem>
                        }
                        @if (_accessRights.Import)
                        {
                            <MudMenuItem>
                                <MudFileUpload T="IBrowserFile" FilesChanged="ImportDataAsync" Accept=".xlsx">
                                    <ActivatorContent>
                                        <MudButton Class="pa-0 ma-0" Style="font-weight:400;text-transform:none;"
                                                   Variant="Variant.Text"
                                                   Disabled="@_isUploading">
                                            @ConstantString.Import
                                        </MudButton>
                                    </ActivatorContent>
                                </MudFileUpload>
                            </MudMenuItem>
                        }
                    </MudMenu>
                </MudToolBar>
                <MudStack Row Spacing="1">
                    @if (_accessRights.Search)
                    {
                        <MudSelect T="string" Placeholder="@Localizer["Search by role name"]" Value="@_searchRole" Clearable="true" ValueChanged="SearchByRole" Style="width:150px">
                            @foreach (var role in _roles.Select(r => r.Name).Distinct())
                            {
                                <MudSelectItem Value="@role">@role</MudSelectItem>
                            }
                        </MudSelect>
                        <MudTextField T="string" Immediate="false" ValueChanged="SearchUsers" Value="@_searchString" Placeholder="@ConstantString.Search" Adornment="Adornment.End"
                                      AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small">
                        </MudTextField>
                    }
                </MudStack>
            </MudStack>
        </MudStack>
    </ToolBarContent>
    <Columns>
        <SelectColumn ShowInFooter="false" />
        <TemplateColumn HeaderStyle="width:60px" Title="@ConstantString.Actions" Sortable="false">
            <CellTemplate>
                @if (_accessRights.Edit || _accessRights.Delete || _accessRights.ManageRoles ||
                                _accessRights.RestPassword || _accessRights.SendRestPasswordMail || _accessRights.ManagePermissions)
                {
                    <MudMenu Icon="@Icons.Material.Filled.Edit" Variant="Variant.Filled" Size="Size.Small"
                             Dense="true"
                             EndIcon="@Icons.Material.Filled.KeyboardArrowDown" IconColor="Color.Info" AnchorOrigin="Origin.CenterLeft">
                        @if (_accessRights.Edit)
                        {
                            <MudMenuItem OnClick="@(() => EditUser(context.Item))">@ConstantString.Edit</MudMenuItem>
                        }
                        @if (_accessRights.Delete)
                        {
                            <MudMenuItem OnClick="@(() => DeleteUserAsync(context.Item))">@ConstantString.Delete</MudMenuItem>
                        }
                        @if (_accessRights.ManagePermissions)
                        {
                            <MudMenuItem OnClick="@(() => OpenPermissionsDrawerAsync(context.Item))">@Localizer["Set Permissions"]</MudMenuItem>
                        }
                        @if (_accessRights.SendRestPasswordMail)
                        {
                            <MudMenuItem OnClick="@(() => SendResetPasswordEmail(context.Item))">@Localizer["Send Reset Password Email"]</MudMenuItem>
                        }
                        @if (_accessRights.RestPassword)
                        {
                            <MudMenuItem Disabled="@(!context.Item.IsActive)" OnClick="@(() => ResetPasswordDialog(context.Item))">@Localizer["Reset Password"]</MudMenuItem>
                        }
                    </MudMenu>
                }
                else
                {
                    <MudButton Variant="Variant.Filled"
                               StartIcon="@Icons.Material.Filled.DoNotTouch" IconColor="Color.Secondary" Size="Size.Small"
                               Color="Color.Surface">
                        @ConstantString.NoAllowed
                    </MudButton>
                }
            </CellTemplate>
        </TemplateColumn>
        <PropertyColumn Property="x => x.Tenant" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.Tenant)]">
            <CellTemplate>
                <MudText>@context.Item.Tenant?.Name</MudText>
            </CellTemplate>
            <FooterTemplate>
                @Localizer["Selected"]: @_selectedUsers.Count
            </FooterTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.UserName" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.UserName)]">
            <CellTemplate>
                <UserCard Item="@context.Item" OnSendVerify="SendVerificationEmail" DisplayOnlineStatus="@_canViewOnlineStatus"></UserCard>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Email" Title="@Localizer["Full Name"]">
            <CellTemplate>
                <div class="d-flex flex-column">
                    <MudText Typo="Typo.body2">@context.Item.DisplayName</MudText>
                </div>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Superior" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.Superior)]">
            <CellTemplate>
                @context.Item.Superior?.UserName
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Provider" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.Provider)]" />
        <PropertyColumn Sortable="false" Property="x => x.AssignedRoles" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.AssignedRoles)]">
            <CellTemplate>
                @if (context.Item.AssignedRoles is not null)
                {
                    foreach (var role in context.Item.AssignedRoles)
                    {
                        <MudChip Size="Size.Small" Variant="Variant.Text">@role</MudChip>
                    }
                }
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.IsActive" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.IsActive)]">
            <CellTemplate>
                <div>
                    @if (!context.Item.IsActive || (context.Item.LockoutEnd is not null && context.Item.LockoutEnd > DateTime.UtcNow))
                    {
                        <MudTooltip Text="@Localizer["Click to change status to active."]" Delay="300">
                            <MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(() => ToggleUserActiveStatusAsync(context.Item))" ThumbIcon="@Icons.Material.Filled.Close" ThumbIconColor="Color.Error" />
                        </MudTooltip>
                    }
                    else
                    {
                        <MudTooltip Text="@Localizer["Click to change status to inactive."]" Delay="300">
                            <MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(() => ToggleUserActiveStatusAsync(context.Item))" ThumbIcon="@Icons.Material.Filled.Done" ThumbIconColor="Color.Success" />
                        </MudTooltip>
                    }
                </div>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.LastModified" Title="@Localizer[_currentUserDto.GetMemberDescription(x => x.LastModified)]">
            <CellTemplate>
                <UtcToLocal UTCDateTime="@(context.Item.LastModified ?? context.Item.Created)" Format="yyyy-MM-dd" />
            </CellTemplate>
        </PropertyColumn>
    </Columns>

    <PagerContent>
        <MudDataGridPager PageSizeOptions="@(new[] { 10, 15, 30, 50, 100, 500, 1000 })" />
    </PagerContent>
</MudDataGrid>

<PermissionsDrawer OnAssignAllChanged="AssignAllPermissionsAsync" Waiting="@_isProcessing" OnOpenChanged="HandlePermissionsDrawerOpenChanged" Open="_showPermissionsDrawer" Permissions="_permissions" OnAssignChanged="HandlePermissionChangedAsync"></PermissionsDrawer>

@code {
    #region Fields and Service Injection

    [CascadingParameter] private Task<AuthenticationState> AuthState { get; set; } = default!;
    [CascadingParameter] public UserProfile? UserProfile { get; set; }

    // Data management services
    private UserManager<ApplicationUser> _userManager = null!;
    private RoleManager<ApplicationRole> _roleManager = null!;
    private PermissionHelper _permissionHelper = null!;

    // Page state and data
    private int _defaultPageSize = 15;
    private HashSet<ApplicationUserDto> _selectedUsers = new();
    private readonly ApplicationUserDto _currentUserDto = new();
    private string _searchString = string.Empty;
    private string _selectedTenantId = string.Empty;
    private string _title { get; set; } = "Users";
    private IList<PermissionModel> _permissions = new List<PermissionModel>();

    public bool _canViewOnlineStatus;
    private bool _isProcessing;
    private bool _showPermissionsDrawer;
    private bool _isLoading;
    private bool _isExporting;
    private bool _isUploading;
    private List<ApplicationRoleDto> _roles = new();
    private string? _searchRole;

    // Permissions control
    private UsersAccessRights _accessRights = new();

    // DataGrid reference
    private MudDataGrid<ApplicationUserDto> _dataGrid = null!;

    private TimeSpan RefreshInterval => TimeSpan.FromHours(2);

    #endregion

    #region Lifecycle and Initialization

    protected override async Task OnInitializedAsync()
    {
        _title = Localizer[_currentUserDto.GetClassDescription()];
        InitializeServices();
        _accessRights = await PermissionService.GetAccessRightsAsync<UsersAccessRights>();
        _roles = RoleService.DataSource;
    }

    private void InitializeServices()
    {
        _roleManager = ScopedServices.GetRequiredService<RoleManager<ApplicationRole>>();
        _userManager = ScopedServices.GetRequiredService<UserManager<ApplicationUser>>();
        _permissionHelper = ScopedServices.GetRequiredService<PermissionHelper>();
    }

    public void Dispose()
    {
        // Dispose resources if needed
    }

    #endregion

    #region Data Loading and Searching

    private Expression<Func<ApplicationUser, bool>> CreateSearchPredicate()
    {
        return x =>
            (x.UserName!.Contains(_searchString) ||
             x.Email!.Contains(_searchString) ||
             x.DisplayName!.Contains(_searchString) ||
             x.PhoneNumber!.Contains(_searchString) ||
             x.Provider!.Contains(_searchString)) &&
            (_searchRole == null || x.UserRoles.Any(ur => ur.Role.Name == _searchRole)) &&
            (string.IsNullOrEmpty(_selectedTenantId) || x.TenantId == _selectedTenantId);
    }

    private async Task<GridData<ApplicationUserDto>> LoadServerData(GridState<ApplicationUserDto> state)
    {
        try
        {
            _isLoading = true;
            var predicate = CreateSearchPredicate();
            var totalCount = await _userManager.Users.CountAsync(predicate);
            var testd = await _userManager.Users.Where(predicate).Include(x => x.CreatedByUser).Include(x => x.LastModifiedByUser).ToListAsync();
            var data = await _userManager.Users.Where(predicate)
                .Include(x => x.UserRoles).ThenInclude(ur => ur.Role)
                .Include(x => x.CreatedByUser)
                .Include(x => x.LastModifiedByUser)
                .Include(x => x.Superior)
                .EfOrderBySortDefinitions(state)
                .Skip(state.Page * state.PageSize).Take(state.PageSize)
                .ProjectTo<ApplicationUserDto>(Mapper.ConfigurationProvider)
                .ToListAsync();

            return new GridData<ApplicationUserDto> { TotalItems = totalCount, Items = data };
        }
        finally
        {
            _isLoading = false;
        }
    }

    private async Task SearchUsers(string searchText)
    {
        if (_isLoading) return;
        _searchString = searchText.ToLower();
        await _dataGrid.ReloadServerData();
    }

    private async Task SearchByRole(string role)
    {
        if (_isLoading) return;
        _searchRole = role;
        await _dataGrid.ReloadServerData();
    }

    private async Task TenantListViewChanged(string tenantId)
    {
        _selectedTenantId = tenantId;
        if (!string.IsNullOrEmpty(_selectedTenantId))
        {
            _roles = RoleService.DataSource.Where(r => r.TenantId == _selectedTenantId).ToList();
        }
        await _dataGrid.ReloadServerData();
    }

    private async Task RefreshDataGrid()
    {
        _selectedUsers = new HashSet<ApplicationUserDto>();
        await _dataGrid.ReloadServerData();
    }

    #endregion

    #region User Creation, Editing, and Deletion

    private async Task ShowUserDialog(ApplicationUserDto model, string dialogTitle, Func<ApplicationUserDto, Task> processAction)
    {
        var parameters = new DialogParameters<UserFormDialog>
        {
            { x => x.Model, model },
            { x => x.UserProfile, UserProfile }
        };
        var options = new DialogOptions { CloseButton = true, CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
        var dialog = await DialogService.ShowAsync<UserFormDialog>(dialogTitle, parameters, options);
        var result = await dialog.Result;
        if (result is not null && !result.Canceled)
        {
            await processAction(model);
        }
    }

    private async Task CreateUserAsync(ApplicationUserDto model)
    {
        var newUser = new ApplicationUser
        {
            Provider = model.Provider,
            DisplayName = model.DisplayName,
            UserName = model.UserName,
            TenantId = model.TenantId,
            Email = model.Email,
            PhoneNumber = model.PhoneNumber,
            SuperiorId = model.Superior?.Id,
            ProfilePictureDataUrl = model.ProfilePictureDataUrl,
            EmailConfirmed = false,
            IsActive = model.IsActive,
            LanguageCode = model.LanguageCode,
            TimeZoneId = model.TimeZoneId,
            CreatedBy = UserProfile?.UserId,
            Created = DateTime.UtcNow
        };

        var result = await _userManager.CreateAsync(newUser);
        if (!result.Succeeded)
        {
            Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
            return;
        }

        Snackbar.Add(Localizer["New user created successfully."], Severity.Info);
        await AssignRolesToUserAsync(newUser, model.AssignedRoles);
        Logger.LogInformation("User created: {UserName} (ID: {UserId})", newUser.UserName, newUser.Id);
        UserService.Refresh();
        await RefreshDataGrid();
    }

    private async Task UpdateUserAsync(ApplicationUserDto model)
    {
        var user = await _userManager.FindByIdAsync(model.Id!)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        var roles = await _userManager.GetRolesAsync(user);
        if (roles.Any())
        {
            await _userManager.RemoveFromRolesAsync(user, roles);
        }

        user.Email = model.Email;
        user.PhoneNumber = model.PhoneNumber;
        user.ProfilePictureDataUrl = model.ProfilePictureDataUrl;
        user.DisplayName = model.DisplayName;
        user.Provider = model.Provider;
        user.UserName = model.UserName;
        user.IsActive = model.IsActive;
        user.TenantId = model.TenantId;
        user.SuperiorId = model.Superior?.Id;
        user.LanguageCode = model.LanguageCode;
        user.TimeZoneId = model.TimeZoneId;
        user.LastModifiedBy = UserProfile?.UserId;
        user.LastModified = DateTime.UtcNow;
        var updateResult = await _userManager.UpdateAsync(user);
        if (updateResult.Succeeded)
        {
            if (model.AssignedRoles is { Length: > 0 })
            {
                await _userManager.AddToRolesAsync(user, model.AssignedRoles);
            }
            Snackbar.Add(Localizer["User updated successfully."], Severity.Info);
            await RefreshDataGrid();
            UserService.Refresh();
            await OnlineUserTracker.Update(model.Id, model.UserName, model.DisplayName ?? string.Empty, model.ProfilePictureDataUrl ?? string.Empty);
        }
        else
        {
            Snackbar.Add(string.Join(",", updateResult.Errors.Select(e => e.Description)), Severity.Error);
        }
    }

    private async Task AssignRolesToUserAsync(ApplicationUser user, string[]? roles)
    {
        if (roles is not null && roles.Length > 0)
        {
            await _userManager.AddToRolesAsync(user, roles);
        }
    }

    private async Task CreateUser()
    {
        var model = new ApplicationUserDto
        {
            Provider = "Local",
            Email = "",
            UserName = "",
            AssignedRoles = Array.Empty<string>(),
            TimeZoneId = TimeZoneInfo.Local.Id,
            LanguageCode = CultureInfo.CurrentCulture.Name
        };
        await ShowUserDialog(model, Localizer["Create a new user"], CreateUserAsync);
    }

    private async Task EditUser(ApplicationUserDto model) =>
        await ShowUserDialog(model, Localizer["Edit the user"], UpdateUserAsync);

    private async Task ShowDeleteConfirmationAsync(ApplicationUserDto model, Func<Task> onConfirmed)
    {
        var message = string.Format(ConstantString.DeleteConfirmation, model.UserName);
        await DialogServiceHelper.ShowConfirmationDialogAsync(ConstantString.DeleteConfirmationTitle, message, onConfirmed);
    }

    private async Task DeleteUserAsync(ApplicationUserDto model)
    {
        var authState = await AuthState;
        if (model.Id == authState.User.GetUserId())
        {
            Snackbar.Add(Localizer["You cannot delete your own account!"], Severity.Error);
            return;
        }
        await ShowDeleteConfirmationAsync(model, async () =>
        {
            await ProcessUserDeletionAsync(model);
            _selectedUsers.Clear();
            await RefreshDataGrid();
        });
    }

    private async Task ProcessUserDeletionAsync(ApplicationUserDto model)
    {
        var user = await _userManager.FindByIdAsync(model.Id)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        var result = await _userManager.DeleteAsync(user);
        if (result.Succeeded)
        {
            Logger.LogInformation("User deleted: {UserName} (ID: {UserId})", user.UserName, user.Id);
            Snackbar.Add(ConstantString.DeleteSuccess, Severity.Info);
            await RefreshDataGrid();
            UserService.Refresh();
        }
        else
        {
            Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
        }
    }

    private async Task DeleteCheckedUsersAsync()
    {
        var authState = await AuthState;
        if (_selectedUsers.Any(u => u.Id == authState.User.GetUserId()))
        {
            Snackbar.Add(Localizer["You cannot delete your own account!"], Severity.Error);
            return;
        }
        var message = string.Format(ConstantString.DeleteConfirmation, _selectedUsers.Count);
        await DialogServiceHelper.ShowConfirmationDialogAsync(ConstantString.DeleteConfirmationTitle, message, DeleteSelectedUsersAsync);
    }

    private async Task DeleteSelectedUsersAsync()
    {
        var deleteIds = _selectedUsers.Select(u => u.Id).ToArray();
        var usersToDelete = await _userManager.Users.Where(x => deleteIds.Contains(x.Id)).ToListAsync();

        foreach (var user in usersToDelete)
        {
            var result = await _userManager.DeleteAsync(user);
            if (!result.Succeeded)
            {
                Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
                return;
            }
            Logger.LogInformation("User deleted: {UserName} (ID: {UserId})", user.UserName, user.Id);
        }
        Snackbar.Add(ConstantString.DeleteSuccess, Severity.Info);
        await RefreshDataGrid();
        UserService.Refresh();
    }

    #endregion

    #region Email Verification and Password Reset

    private async Task SendVerificationEmail(ApplicationUserDto model)
    {
        var user = await _userManager.FindByIdAsync(model.Id)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["userId"] = model.Id, ["code"] = code, ["returnUrl"] = "/" });
        await Mediator.Publish(new UserActivationNotification(callbackUrl, model.Email, model.Id, model.UserName));
        Snackbar.Add(string.Format(Localizer["Verification email sent to {0}."], model.Email), Severity.Info);
        Logger.LogInformation("Verification email sent to {UserName} (ID: {UserId})", model.UserName, model.Id);
    }

    private async Task SendResetPasswordEmail(ApplicationUserDto model)
    {
        var user = await _userManager.FindByIdAsync(model.Id)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        var code = await _userManager.GeneratePasswordResetTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(ResetPassword.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["userId"] = user.Id, ["token"] = code });
        await Mediator.Publish(new ResetPasswordNotification(callbackUrl, model.Email, model.UserName));
        Snackbar.Add(string.Format(Localizer["A new password for {0} has been sent via email. The user will be required to change it upon first login."], model.UserName), Severity.Info);
    }

    private async Task ResetPasswordDialog(ApplicationUserDto model)
    {
        var resetModel = new ResetPasswordFormModel { Id = model.Id, DisplayName = model.DisplayName, UserName = model.UserName, ProfilePictureDataUrl = model.ProfilePictureDataUrl };
        var parameters = new DialogParameters<ResetPasswordDialog> { { x => x.Model, resetModel } };
        var options = new DialogOptions { CloseOnEscapeKey = true, CloseButton = true, MaxWidth = MaxWidth.ExtraSmall };
        var dialog = await DialogService.ShowAsync<ResetPasswordDialog>(Localizer["Set Password"], parameters, options);
        var result = await dialog.Result;
        if (result is not null && !result.Canceled)
        {
            await ProcessPasswordResetAsync(model, resetModel);
        }
    }

    private async Task ProcessPasswordResetAsync(ApplicationUserDto model, ResetPasswordFormModel resetModel)
    {
        var user = await _userManager.FindByIdAsync(model.Id!)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
        var result = await _userManager.ResetPasswordAsync(user, token, resetModel.Password!);
        if (result.Succeeded)
        {
            if (!user.EmailConfirmed)
            {
                user.EmailConfirmed = true;
                await _userManager.UpdateAsync(user);
            }
            Snackbar.Add(Localizer["Reset password successfully."], Severity.Info);
        }
        else
        {
            Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
        }
    }

    #endregion

    #region User Activation and Permission Management

    private async Task ToggleUserActiveStatusAsync(ApplicationUserDto model)
    {
        var user = await _userManager.FindByIdAsync(model.Id!)
                   ?? throw new NotFoundException($"User not found: {model.Id}");
        if (user.IsActive)
            await DeactivateUserAsync(user, model);
        else
            await ActivateUserAsync(user, model);
    }

    private async Task ActivateUserAsync(ApplicationUser user, ApplicationUserDto model)
    {
        user.IsActive = true;
        user.LockoutEnd = null;
        user.LastModified = DateTime.UtcNow;
        user.LastModifiedBy = UserProfile?.UserId;
        var result = await _userManager.UpdateAsync(user);
        if (result.Succeeded)
        {
            model.IsActive = true;
            model.LockoutEnd = null;
            Snackbar.Add(Localizer["The user has been activated."], Severity.Info);
        }
        else
        {
            Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
        }
    }

    private async Task DeactivateUserAsync(ApplicationUser user, ApplicationUserDto model)
    {
        user.IsActive = false;
        user.LockoutEnd = DateTimeOffset.MaxValue;
        user.LastModified = DateTime.UtcNow;
        user.LastModifiedBy = UserProfile?.UserId;
        var result = await _userManager.UpdateAsync(user);
        if (result.Succeeded)
        {
            model.IsActive = false;
            model.LockoutEnd = DateTimeOffset.MaxValue;
            Snackbar.Add(Localizer["The user has been inactivated."], Severity.Info);
        }
        else
        {
            Snackbar.Add(string.Join(",", result.Errors.Select(e => e.Description)), Severity.Error);
        }
    }

    private async Task OpenPermissionsDrawerAsync(ApplicationUserDto model)
    {
        _showPermissionsDrawer = true;
        _permissions = await _permissionHelper.GetAllPermissionsByUserId(model.Id);
    }

    private Task HandlePermissionsDrawerOpenChanged(bool state)
    {
        _showPermissionsDrawer = state;
        return Task.CompletedTask;
    }

    private async Task AssignAllPermissionsAsync(List<PermissionModel> models)
    {
        _isProcessing = true;
        try
        {
            var userId = models.First().UserId;
            var user = await _userManager.FindByIdAsync(userId!)
                       ?? throw new NotFoundException($"User not found: {userId}");
            foreach (var model in models)
            {
                await UpdateUserPermissionAsync(user, model);
            }
            Snackbar.Add(Localizer["Authorization has been changed."], Severity.Info);
            await ClearUserClaimsCacheAsync(user.Id);
        }
        finally
        {
            _isProcessing = false;
        }
    }

    private async Task UpdateUserPermissionAsync(ApplicationUser user, PermissionModel model)
    {
        if (model.Assigned)
        {
            if (!string.IsNullOrEmpty(model.ClaimType) && !string.IsNullOrEmpty(model.ClaimValue))
                await _userManager.AddClaimAsync(user, new Claim(model.ClaimType, model.ClaimValue));
        }
        else
        {
            var existingClaim = (await _userManager.GetClaimsAsync(user)).FirstOrDefault(c => c.Value == model.ClaimValue);
            if (existingClaim is not null)
                await _userManager.RemoveClaimAsync(user, existingClaim);
        }
    }

    private async Task ClearUserClaimsCacheAsync(string userId)
    {
        var cacheKey = $"get-claims-by-{userId}";
        FusionCache.Remove(cacheKey);
        await Task.Delay(300);
    }

    private async Task HandlePermissionChangedAsync(PermissionModel model)
    {
        _isProcessing = true;
        try
        {
            var userId = model.UserId!;
            var user = await _userManager.FindByIdAsync(userId)
                       ?? throw new NotFoundException($"User not found: {userId}");
            model.Assigned = !model.Assigned;
            if (model.Assigned)
            {
                if (!string.IsNullOrEmpty(model.ClaimType) && !string.IsNullOrEmpty(model.ClaimValue))
                {
                    await _userManager.AddClaimAsync(user, new Claim(model.ClaimType, model.ClaimValue));
                    Snackbar.Add(Localizer["Permission assigned successfully."], Severity.Info);
                }
            }
            else
            {
                var existingClaim = (await _userManager.GetClaimsAsync(user)).FirstOrDefault(c => c.Value == model.ClaimValue);
                if (existingClaim is not null)
                    await _userManager.RemoveClaimAsync(user, existingClaim);
                Snackbar.Add(Localizer["Permission removed successfully."], Severity.Info);
            }
            await ClearUserClaimsCacheAsync(user.Id);
        }
        finally
        {
            _isProcessing = false;
        }
    }

    #endregion

    #region Import and Export

    private async Task ExportUsersAsync()
    {
        _isExporting = true;
        try
        {
            var predicate = CreateSearchPredicate();
            var users = await _userManager.Users.Where(predicate)
                .Select(x => new ApplicationUserDto
                {
                    Id = x.Id,
                    UserName = x.UserName!,
                    DisplayName = x.DisplayName,
                    Email = x.Email!,
                    PhoneNumber = x.PhoneNumber,
                    LanguageCode = x.LanguageCode,
                    TimeZoneId = x.TimeZoneId,
                    SuperiorId = x.SuperiorId,
                    TenantId = x.TenantId
                }).ToListAsync();

            var fileData = await ExportUsersToExcelAsync(users);
            await DownloadFileService.DownloadFileAsync($"{Localizer["Users"]}.xlsx", fileData, "application/octet-stream");
            Snackbar.Add(ConstantString.ExportSuccess, Severity.Info);
        }
        finally
        {
            _isExporting = false;
        }
    }

    private async Task<byte[]> ExportUsersToExcelAsync(List<ApplicationUserDto> users) =>
        await ExcelService.ExportAsync(users, new Dictionary<string, Func<ApplicationUserDto, object?>>
        {
            { Localizer["Id"], item => item.Id },
            { Localizer["User Name"], item => item.UserName },
            { Localizer["Full Name"], item => item.DisplayName },
            { Localizer["Email"], item => item.Email },
            { Localizer["Phone Number"], item => item.PhoneNumber },
            { Localizer["Time Zone"], item => item.TimeZoneId },
            { Localizer["Language"], item => item.LanguageCode },
            { Localizer["Tenant Id"], item => item.TenantId },
            { Localizer["Superior Id"], item => item.SuperiorId }
        }, Localizer["Users"]);

    private async Task ImportDataAsync(IBrowserFile file)
    {
        _isUploading = true;
        try
        {
            using var stream = new MemoryStream();
            await file.OpenReadStream(GlobalVariable.MaxAllowedSize).CopyToAsync(stream);
            var importResult = await ImportUsersFromExcelAsync(stream);
            if (importResult?.Succeeded == true)
            {
                if (importResult.Data != null)
                {
                    await ProcessImportedUsersAsync(importResult.Data);
                    await _dataGrid.ReloadServerData();
                }
                Snackbar.Add(ConstantString.ImportSuccess, Severity.Info);
            }
            else if (importResult?.Errors != null)
            {
                foreach (var error in importResult.Errors)
                {
                    Snackbar.Add(error, Severity.Error);
                }
            }
        }
        finally { _isUploading = false; }
    }

    private async Task<IResult<IEnumerable<ApplicationUser>>> ImportUsersFromExcelAsync(MemoryStream stream) =>
        await ExcelService.ImportAsync(stream.ToArray(), new Dictionary<string, Func<DataRow, ApplicationUser, object?>>
        {
            { Localizer["User Name"], (row, item) => item.UserName = row[Localizer["User Name"]]?.ToString() },
            { Localizer["Full Name"], (row, item) => item.DisplayName = row[Localizer["Full Name"]]?.ToString() },
            { Localizer["Email"], (row, item) => item.Email = row[Localizer["Email"]]?.ToString() },
            { Localizer["Phone Number"], (row, item) => item.PhoneNumber = row[Localizer["Phone Number"]]?.ToString() },
            { Localizer["Time Zone"], (row, item) => item.TimeZoneId = row[Localizer["Time Zone"]]?.ToString() },
            { Localizer["Language"], (row, item) => item.LanguageCode = row[Localizer["Language"]]?.ToString() },
            { Localizer["Tenant Id"], (row, item) => item.TenantId = row[Localizer["Tenant Id"]]?.ToString() },
            { Localizer["Superior Id"], (row, item) => item.SuperiorId = row[Localizer["Superior Id"]]?.ToString() }
        }, Localizer["Users"]);

    private async Task ProcessImportedUsersAsync(IEnumerable<ApplicationUser> users)
    {
        foreach (var user in users)
        {
            if (!_userManager.Users.Any(x => x.UserName == user.UserName))
            {
                var result = await _userManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    await _userManager.AddToRolesAsync(user, new[] { RoleName.Basic });
                }
                else
                {
                    Snackbar.Add(string.Join(',', result.Errors.Select(e => e.Description)), Severity.Error);
                }
            }
        }
    }

    #endregion

    #region Welcome Email

    private async Task SendWelcomeEmailNotificationAsync(string email, string userName)
    {
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(Login.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["returnUrl"] = "/" });

        await Mediator.Publish(new SendWelcomeNotification(callbackUrl, email, userName));
        Logger.LogInformation("{UserName} activated successfully!", email);
    }

    #endregion
}
